﻿#pragma warning disable 0168
using System;
using System.Collections.Generic;
using System.Linq;
using Celeriq.Client.Interfaces;
using Celeriq.Server.Interfaces;
using Celeriq.Utilities;
using System.IO;
using Celeriq.Common;
using System.ServiceModel;
using System.Runtime.Serialization;
using System.ServiceModel.Description;
using System.Security.Permissions;
using System.Security;
using System.Diagnostics;
using System.Threading.Tasks;

namespace Celeriq.Server.Core
{
    [Serializable()]
    [KnownType(typeof (RepositoryDefinition))]
    [KnownType(typeof (FieldDefinition))]
    [KnownType(typeof (DimensionDefinition))]
    [KnownType(typeof (IFieldDefinition))]
    [KnownType(typeof (Celeriq.Client.Interfaces.IRemotingObject))]
    [KnownType(typeof (Celeriq.Client.Interfaces.BaseRemotingObject))]
    [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.Single)]
    public class SystemCore : MarshalByRefObject, ISystemCore, IDisposable
    {
        #region Class Members

        private List<RemotingObjectCache> _repositoryActives = new List<RemotingObjectCache>();
        private System.Timers.Timer _timerDelayLoad = null;
        private System.Timers.Timer _timer = null;
        private ServerResourceSettings _resourceSettings = null;

        #endregion

        #region Constructors

        public SystemCore()
            : base()
        {
            _timerDelayLoad = new System.Timers.Timer(2000);
            _timerDelayLoad.Elapsed += DelayStartup;
            _timerDelayLoad.Start();
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="restart">Determines if previously loaded repositories should be restarted automatically</param>
        public SystemCore(bool restart)
            : this()
        {
            if (restart)
                RestartRepositories();
        }

        private void DelayStartup(object sender, System.Timers.ElapsedEventArgs e)
        {
            try
            {
                _timerDelayLoad.Stop();
                _timerDelayLoad = null;

                lock (_repositoryActives)
                {
                    #region HouseKeeping

                    //Initialize user file if need be
                    InitUserFile();

                    #endregion

                    _resourceSettings = new ServerResourceSettings();
                    _resourceSettings.Initialize();

                    //Verify that all permissions are in place
                    if (!this.IsSetupValid())
                    {
                        throw new Exception("This application does not have the proper permissions!");
                    }

                    var timer = new Stopwatch();
                    timer.Start();

                    var files = Directory.GetFiles(ConfigurationSettings.DataPath, "*.celeriq");
                    Logger.LogInfo("Loading Data Path: '" + ConfigurationSettings.DataPath + "'. Repositories found: " + files.Length);

                    foreach (var f in files)
                    {
                        var repository = new RepositoryDefinition();
                        repository.Load(f);
                        repository.CachePath = Path.Combine(ConfigurationSettings.DataPath, repository.ID.ToString());
                        var remoteItem = new RemotingObjectCache() { Repository = repository, VersionHash = repository.VersionHash };
                        _repositoryActives.Add(remoteItem);
                        StartRepository(remoteItem.Repository.ID, this.RootUser);
                    }

                    timer.Stop();
                    Console.WriteLine("Core loaded " + timer.ElapsedMilliseconds + " ms");
                }

                _timer = new System.Timers.Timer(20000);
                _timer.Elapsed += _timer_Elapsed;
                _timer.Start();
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        #endregion

        #region Event Handlers

        private void _timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            try
            {
                lock (_repositoryActives)
                {
                    #region This will unload the data in a repository if there is a set # of minutes to keep inactive repository

                    var wasUnloaded = false;
                    foreach (var item in _repositoryActives.Where(x => x.GetIsLoaded()))
                    {
                        if (item.ServiceInstance.LastAccess != DateTime.MinValue &&
                            (_resourceSettings.AutoDataUnloadTime > 0) &&
                            (DateTime.Now.Subtract(item.ServiceInstance.LastAccess).TotalMinutes >= _resourceSettings.AutoDataUnloadTime))
                        {
                            item.ServiceInstance.UnloadData();
                            wasUnloaded = true;
                        }
                    }

                    #endregion

                    #region Unload if thre are too many repositories in memory

                    if (_resourceSettings.MaxRunningRepositories > 0)
                    {
                        var tooManyList = _repositoryActives
                            .Where(x => x.GetIsLoaded())
                            .OrderByDescending(x => x.ServiceInstance.LastAccess)
                            .Skip(_resourceSettings.MaxRunningRepositories)
                            .ToList();

                        foreach (var item in tooManyList)
                        {
                            item.ServiceInstance.UnloadData();
                            wasUnloaded = true;
                        }
                    }

                    #endregion

                    #region Unload if used too much memory

                    if (_resourceSettings.MaxMemory > 0)
                    {
                        long TOTAL = _resourceSettings.MaxMemory*1024*1024;
                        var list = _repositoryActives.Where(x => x.GetIsLoaded()).OrderByDescending(x => x.ServiceInstance.LastAccess).ToList();
                        long usedMemory = 0;
                        foreach (var item in list)
                        {
                            usedMemory += item.ServiceInstance.GetDataMemorySize(RootUser);
                            if (usedMemory > TOTAL)
                            {
                                item.ServiceInstance.UnloadData();
                                wasUnloaded = true;
                            }
                        }
                    }

                    #endregion

                    //collect garbage to free memory
                    if (wasUnloaded)
                    {
                        GC.Collect();
                    }

                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        #endregion

        #region Properties

        private object _cacheCryptedLocker = new object();
        private SystemCredentials _cacheCrypted = null;

        private SystemCredentials RootUser
        {
            get
            {
                try
                {
                    lock (_cacheCryptedLocker)
                    {
                        if (_cacheCrypted == null)
                        {
                            var user = UserDomain.UserList.FirstOrDefault(x => x.UserName == "root");
                            if (user == null) return null;
                            user = new SystemCredentials() {UserName = user.UserName, Password = user.Password};
                            user.Password = Celeriq.Utilities.SecurityHelper.Encrypt(this.GetPublicKey(), user.Password);
                            _cacheCrypted = user;
                            return user;
                        }
                        else
                        {
                            return _cacheCrypted;
                        }
                    }
                }
                catch (Exception ex)
                {
                    Logger.LogError(ex);
                    throw;
                }
            }
        }

        public int GetRepositoryCount(UserCredentials credentials)
        {
            lock (_repositoryActives)
            {
                return _repositoryActives.Count;
            }
        }

        public List<BaseRemotingObject> GetRepositoryPropertyList(UserCredentials credentials, int pageOffSet, int recordsPerPage)
        {
            if (!IsValidCredentials(credentials))
            {
                Logger.LogInfo("GetRepositoryPropertyList: Invalid credentials");
                throw new Exception("Invalid credentials");
            }

            if (pageOffSet < 1)
            {
                throw new Exception("PageOffset must be greater than 0.");
            }

            if (recordsPerPage < 1)
            {
                throw new Exception("RecordsPerPage must be greater than 0.");
            }

            Logger.LogInfo("GetRepositoryPropertyList Call Start");
            try
            {
                lock (_repositoryActives)
                {
                    var startIndex = (pageOffSet - 1) * recordsPerPage;
                    var tempList = _repositoryActives.OrderBy(x => x.Repository.Name).Skip(startIndex).Take(recordsPerPage).ToList();
                    foreach (var item in tempList)
                    {
                        var allZero = ((item.ItemCount + item.DataDiskSize + item.DataMemorySize) == 0);
                        if (item.IsRunning && allZero)
                        {
                            item.ItemCount = item.ServiceInstance.GetItemCount(credentials);
                            item.DataDiskSize = item.ServiceInstance.GetDataDiskSize(credentials);
                            item.DataMemorySize = item.ServiceInstance.GetDataMemorySize(credentials);
                        }
                    }

                    tempList.ForEach(x => x.IsLoaded = x.GetIsLoaded());
                    var retval = new List<BaseRemotingObject>();
                    tempList.ForEach(x => retval.Add((BaseRemotingObject)((ICloneable)x).Clone()));
                    return retval;
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                return null;
            }
            finally
            {
                Logger.LogInfo("GetRepositoryPropertyList Call End");
            }
        }

        #endregion

        #region Methods

        public string GetPublicKey()
        {
            return ConfigurationSettings.MasterKeys.PublicKey;
        }

        private static readonly Dictionary<string, string> _keyCache = new Dictionary<string, string>();

        public bool IsValidCredentials(UserCredentials credentials)
        {
            try
            {
                lock (_keyCache)
                {
                    var user = UserDomain.UserList.FirstOrDefault(x => x.UserName == credentials.UserName);
                    if (user == null) return false;

                    var prehash = string.Empty;

                    var cacheKey = ConfigurationSettings.MasterKeys.PrivateKey + "||" + credentials.Password;
                    if (_keyCache.ContainsKey(cacheKey))
                    {
                        prehash = _keyCache[cacheKey];
                    }
                    else
                    {
                        prehash = Celeriq.Utilities.SecurityHelper.Decrypt(ConfigurationSettings.MasterKeys.PrivateKey, credentials.Password);

                        //Cache
                        _keyCache.Add(cacheKey, prehash);
                    }
                    return (prehash == user.Password);
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                return false;
            }
        }

        private void InitUserFile()
        {
            try
            {
                if (UserDomain.UserList.Count == 0)
                {
                    UserDomain.AddUser(new SystemCredentials {UserName = "root", Password = "password"});
                }

            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        private bool IsSetupValid()
        {
            var writePermission = new FileIOPermission(FileIOPermissionAccess.Write, ConfigurationSettings.DataPath);
            if (!SecurityManager.IsGranted(writePermission))
            {
                Logger.LogError("The service does not have permission to access the install folder!");
                return false;
            }

            var eventLogPermission = new System.Diagnostics.EventLogPermission();
            try
            {
                eventLogPermission.Demand();
            }
            catch (Exception ex)
            {
                Logger.LogError("The service does not have permission to the event log!");
                return false;
            }
            return true;
        }

        public BaseRemotingObject SaveRepository(RepositoryDefinition repository, UserCredentials credentials)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            lock (_repositoryActives)
            {
                if (repository == null)
                    throw new Exception("Object cannot be null!");

                Logger.LogInfo("SaveRepository: " + repository.ID);
                if (repository.CreatedDate == DateTime.MinValue)
                    repository.CreatedDate = DateTime.Now;

                try
                {
                    var fileName = Path.Combine(ConfigurationSettings.DataPath, repository.ID.ToString() + ".celeriq");
                    repository.ToDisk(fileName);

                    var active = _repositoryActives.FirstOrDefault(x => x.Repository.ID == repository.ID);
                    if (active == null)
                    {
                        active = new RemotingObjectCache() {Repository = repository, VersionHash = repository.VersionHash};
                        _repositoryActives.Add(active);
                        StartRepository(active.Repository.ID, this.RootUser);
                    }
                    else
                    {
                        //Save state
                        var isOn = active.IsRunning;

                        if (!active.IsRunning)
                            StartRepository(active.Repository.ID, this.RootUser);

                        active.Repository = new RepositoryDefinition();
                        active.Repository.Load(fileName);

                        //For now lets just try to stop and start the repository
                        ResetRepository(repository.ID);
                        active.VersionHash = repository.VersionHash;

                        if (!isOn)
                            StopRepository(active.Repository.ID, this.RootUser);
                    }

                    return ((ICloneable) ((BaseRemotingObject) active)).Clone() as BaseRemotingObject;
                }
                catch (Exception ex)
                {
                    Logger.LogError(ex);
                    return null;
                }
            }
        }

        private void UnloadOldest()
        {
            var item = _repositoryActives
                .Where(x => x.IsRunning)
                .OrderBy(x => x.ServiceInstance.LastAccess)
                .FirstOrDefault();

            if (item != null)
            {
                item.ServiceInstance.UnloadData();
            }
        }

        private int LoadedCount()
        {
            try
            {
                return _repositoryActives.Count(x => x.IsRunning && x.GetIsLoaded());

            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public bool RepositoryExists(Guid serverId, UserCredentials credentials)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            try
            {
                lock (_repositoryActives)
                {
                    return _repositoryActives.Any(x => x.Repository.ID == serverId);
                }

            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public BaseRemotingObject DataLoadRepository(Guid serverId, UserCredentials credentials)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            try
            {
                lock (_repositoryActives)
                {
                    var active = _repositoryActives.FirstOrDefault(x => x.Repository.ID == serverId && x.IsRunning);
                    if (active == null) return null; //cannot find repository, should never happen
                    if (active.ServiceInstance == null) return null; //cannot find repository, should never happen
                    if (active.GetIsLoaded()) return ((ICloneable) ((BaseRemotingObject) active)).Clone() as BaseRemotingObject;
                    active.ServiceInstance.LoadData(credentials);
                    return ((ICloneable) ((BaseRemotingObject) active)).Clone() as BaseRemotingObject;
                }

            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public BaseRemotingObject StartRepository(Guid serverId, UserCredentials credentials)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            lock (_repositoryActives)
            {
                RemotingObjectCache active = null;
                try
                {
                    //If there is a maximum # of repository defined then stop least used if necessary
                    if ((_resourceSettings.MaxRunningRepositories > 0) && LoadedCount() >= _resourceSettings.MaxRunningRepositories)
                        UnloadOldest();

                    active = _repositoryActives.FirstOrDefault(x => x.Repository.ID == serverId);
                    if (active == null) return null; //cannot find repository, should never happen
                    if (active.Host != null)
                        return ((ICloneable) ((BaseRemotingObject) active)).Clone() as BaseRemotingObject; //if already has a host object then it is active, do not restart
                }
                catch (Exception ex)
                {
                    Logger.LogError(ex);
                    throw;
                }

                try
                {
                    //Load Server Object
                    var baseAddress = new Uri("net.tcp://localhost:1973/" + active.Repository.ID.ToString());
                    var serviceInstance = new Celeriq.RepositoryAPI.RepositoryService();

                    //Load data into this service object
                    var startUp = new Celeriq.Client.Interfaces.ServiceStartup()
                                      {
                                          RepositoryFolder = ConfigurationSettings.DataPath,
                                          RepositoryDefinition = active.Repository,
                                          CacheLength = 300,
                                      };
                    var count = serviceInstance.StartUp(startUp);
                    var host = new ServiceHost(serviceInstance, baseAddress);

                    //Initialize the service
                    var myBinding = new NetTcpBinding()
                                        {
                                            MaxBufferSize = int.MaxValue,
                                            MaxReceivedMessageSize = int.MaxValue,
                                            MaxBufferPoolSize = int.MaxValue,
                                            ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas()
                                                               {
                                                                   MaxArrayLength = int.MaxValue,
                                                                   MaxBytesPerRead = int.MaxValue,
                                                                   MaxDepth = int.MaxValue,
                                                                   MaxNameTableCharCount = int.MaxValue,
                                                                   MaxStringContentLength = int.MaxValue,
                                                               }
                                        };
                    myBinding.Security.Mode = SecurityMode.None;
                    var endpoint = host.AddServiceEndpoint(typeof (Celeriq.Common.IDataModel), myBinding, host.BaseAddresses.First().AbsoluteUri);

                    foreach (var op in endpoint.Contract.Operations)
                    {
                        var dataContractBehavior = op.Behaviors.Find<DataContractSerializerOperationBehavior>();
                        if (dataContractBehavior != null)
                        {
                            dataContractBehavior.MaxItemsInObjectGraph = int.MaxValue;
                        }
                    }

                    //var behavior = host.Description.Behaviors.Find<ServiceDebugBehavior>();
                    //behavior.IncludeExceptionDetailInFaults = true;

                    host.Open();
                    active.Host = host;
                    active.ItemCount = count;
                    active.ServiceInstance = serviceInstance as IRepositoryService;
                    active.DataDiskSize = active.ServiceInstance.GetDataDiskSize(credentials);
                    active.DataMemorySize = active.ServiceInstance.GetDataMemorySize(credentials);
                    active.IsRunning = true;

                    Logger.LogInfo("Server Started '" + serverId.ToString() + "' (" + count + " items)");

                    return ((ICloneable) ((BaseRemotingObject) active)).Clone() as BaseRemotingObject;
                }
                catch (Exception ex)
                {
                    Logger.LogError(ex);
                    throw;
                }
            }

        }

        public BaseRemotingObject StopRepository(Guid serverId, UserCredentials credentials)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            lock (_repositoryActives)
            {
                try
                {
                    var active = _repositoryActives.FirstOrDefault(x => x.Repository.ID == serverId);
                    if (active == null) return null; //cannot find repository, should never happen
                    if (active.Host == null)
                        return ((ICloneable) ((BaseRemotingObject) active)).Clone() as BaseRemotingObject; //if already stopped do not perform again

                    if (active.Host != null)
                    {
                        active.ServiceInstance.ShutDown();
                        active.Host.Close();
                        active.Host = null;
                        active.ServiceInstance = null;
                        active.IsRunning = false;
                        Logger.LogInfo("Server Stopped '" + serverId.ToString() + "'");
                    }
                    return ((ICloneable) ((BaseRemotingObject) active)).Clone() as BaseRemotingObject;
                }
                catch (Exception ex)
                {
                    Logger.LogError(ex);
                    throw;
                }
            }

        }

        public void DeleteRepository(RepositoryDefinition repository, UserCredentials credentials)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            try
            {
                lock (_repositoryActives)
                {
                    var active = _repositoryActives.FirstOrDefault(x => x.Repository.ID == repository.ID);
                    if (active != null)
                    {
                        StopRepository(repository.ID, credentials);

                        var dataPath = Path.Combine(ConfigurationSettings.DataPath, repository.ID.ToString());
                        if (Directory.Exists(dataPath))
                            Directory.Delete(dataPath, true);
                        var fileName = Path.Combine(ConfigurationSettings.DataPath, repository.ID.ToString() + ".celeriq");
                        if (File.Exists(fileName))
                            File.Delete(fileName);

                        _repositoryActives.Remove(active);
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public void Compress(Guid serverId, UserCredentials credentials)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            lock (_repositoryActives)
            {
                var active = _repositoryActives.FirstOrDefault(x => x.Repository.ID == serverId);
                if (active == null) return; //cannot find repository, should never happen

                Logger.LogInfo("Compressing Repository: " + serverId.ToString());
                try
                {
                    active.ServiceInstance.Compress(credentials);
                }
                catch (Exception ex)
                {
                    Logger.LogError(ex);
                    throw;
                }

                Logger.LogInfo("Compressed Repository: " + serverId.ToString());
            }
        }

        /// <summary>
        /// Deletes all data from a repository and starts fresh
        /// </summary>
        /// <param name="serverId"></param>
        /// <returns></returns>
        private bool ResetRepository(Guid serverId)
        {
            lock (_repositoryActives)
            {
                var active = _repositoryActives.FirstOrDefault(x => x.Repository.ID == serverId);
                if (active == null) return false; //cannot find repository, should never happen

                Logger.LogInfo("Resetting Repository: " + serverId.ToString());
                try
                {
                    var startUp = new Celeriq.Client.Interfaces.ServiceStartup()
                                      {
                                          RepositoryFolder = ConfigurationSettings.DataPath,
                                          RepositoryDefinition = active.Repository,
                                          CacheLength = 300,
                                      };

                    if (active.ServiceInstance == null)
                    {
                        var serviceInstance = new Celeriq.RepositoryAPI.RepositoryService();
                        serviceInstance.Reset(startUp);
                    }
                    else
                    {
                        active.ServiceInstance.Reset(startUp);
                    }

                }
                catch (Exception ex)
                {
                    Logger.LogError(ex);
                    throw;
                }

                Logger.LogInfo("Reset Repository: " + serverId.ToString());
                return true;
            }
        }

        /// <summary>
        /// Shuts down all repositories
        /// </summary>
        public void ShutDown()
        {
            try
            {
                lock (_repositoryActives)
                {
                    var stateConfig = new RepositoryStateConfiguration();
                    stateConfig.RunningRepositories.Clear();

                    Logger.LogInfo("ShutDown Start of " + _repositoryActives.Count + " items");
                    foreach (var item in _repositoryActives)
                    {
                        if (item.IsRunning) stateConfig.RunningRepositories.Add(item.Repository.ID);
                        this.StopRepository(item.Repository.ID, this.RootUser);
                    }
                    stateConfig.Save();
                    ConfigurationSettings.Save();
                    Logger.LogInfo("ShutDown Complete");
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        /// <summary>
        /// Restart all repositories that were loaded on the last shutdown
        /// </summary>
        public void RestartRepositories()
        {
            lock (_repositoryActives)
            {
                try
                {
                    //Reload Running Repositories
                    var stateConfig = new RepositoryStateConfiguration();
                    foreach (var g in stateConfig.RunningRepositories)
                    {
                        var item = _repositoryActives.FirstOrDefault(x => x.Repository.ID == g);
                        if (item != null) StartRepository(g, this.RootUser);
                    }
                }
                catch (Exception ex)
                {
                    Logger.LogError(ex);
                    throw;
                }
            }
        }

        public bool ExportSchema(Guid repositoryId, UserCredentials credentials, string backupFile)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            try
            {
                lock (_repositoryActives)
                {
                    var active = _repositoryActives.FirstOrDefault(x => x.Repository.ID == repositoryId);
                    if (active == null)
                    {
                        return false;
                    }

                    var startUp = new Celeriq.Client.Interfaces.ServiceStartup()
                                      {
                                          RepositoryFolder = ConfigurationSettings.DataPath,
                                          RepositoryDefinition = active.Repository,
                                          CacheLength = 300,
                                      };

                    var instance = active.ServiceInstance;
                    if (instance == null)
                        instance = new Celeriq.RepositoryAPI.RepositoryService();

                    instance.ExportSchema(startUp, credentials, backupFile);
                    return true;
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public bool Backup(Guid repositoryId, UserCredentials credentials, string backupFile)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            try
            {
                lock (_repositoryActives)
                {
                    var active = _repositoryActives.FirstOrDefault(x => x.Repository.ID == repositoryId);
                    if (active == null)
                    {
                        return false;
                    }

                    var startUp = new Celeriq.Client.Interfaces.ServiceStartup()
                                      {
                                          RepositoryFolder = ConfigurationSettings.DataPath,
                                          RepositoryDefinition = active.Repository,
                                          CacheLength = 300,
                                      };

                    var instance = active.ServiceInstance;
                    if (instance == null)
                        instance = new Celeriq.RepositoryAPI.RepositoryService();

                    instance.Backup(startUp, credentials, backupFile);
                    return true;
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public bool Restore(Guid repositoryId, UserCredentials credentials, string backupFile)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            try
            {
                lock (_repositoryActives)
                {
                    var active = _repositoryActives.FirstOrDefault(x => x.Repository.ID == repositoryId);
                    if (active == null)
                    {
                        return false;
                    }

                    var startUp = new Celeriq.Client.Interfaces.ServiceStartup()
                                      {
                                          RepositoryFolder = ConfigurationSettings.DataPath,
                                          RepositoryDefinition = active.Repository,
                                          CacheLength = 300,
                                      };

                    var instance = active.ServiceInstance;
                    if (instance == null)
                        instance = new Celeriq.RepositoryAPI.RepositoryService();
                    instance.Restore(startUp, credentials, backupFile);
                    return true;
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public string[] GetBackups(UserCredentials credentials)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            try
            {
                var retval = new List<string>();
                var backupFolder = Path.Combine(ConfigurationSettings.DataPath, "..", "Backup");
                if (Directory.Exists(backupFolder))
                {
                    var files = Directory.GetFiles(backupFolder);
                    foreach (var file in files)
                    {
                        var fi = new FileInfo(file);
                        retval.Add(fi.Name);
                    }
                }
                return retval.ToArray();
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public ServerResourceSettings GetServerResourceSetting(UserCredentials credentials)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            lock (_repositoryActives)
            {
                return _resourceSettings;
            }
        }

        public bool SaveServerResourceSetting(UserCredentials credentials, ServerResourceSettings settings)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            try
            {
                lock (_repositoryActives)
                {
                    _resourceSettings = settings;
                    _resourceSettings.Save();
                    return true;
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public ProfileItem[] GetProfile(Guid repositoryId, UserCredentials credentials, long lastProfileId)
        {
            if (!IsValidCredentials(credentials))
                throw new Exception("Invalid credentials");

            try
            {
                var active = _repositoryActives.FirstOrDefault(x => x.Repository.ID == repositoryId);
                if (active == null || active.ServiceInstance == null)
                {
                    return new ProfileItem[] {};
                }
                else if (!active.ServiceInstance.IsLoaded)
                {
                    return new ProfileItem[] {};
                }
                return active.ServiceInstance.GetProfile(credentials, lastProfileId);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public bool AddSystemUser(UserCredentials credentials, UserCredentials user)
        {
            try
            {
                lock (_repositoryActives)
                {
                    return UserDomain.AddUser(new SystemCredentials() {UserName = user.UserName, Password = user.Password});
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public bool DeleteSystemUser(UserCredentials credentials, UserCredentials user)
        {
            try
            {
                lock (_repositoryActives)
                {
                    return UserDomain.DeleteUser(new SystemCredentials() {UserName = user.UserName, Password = user.Password});
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public UserCredentials[] GetUserList(UserCredentials credentials)
        {
            try
            {
                lock (_repositoryActives)
                {
                    var retval = new List<UserCredentials>();
                    UserDomain.UserList.ToList().ForEach(x => retval.Add((UserCredentials) ((ICloneable) x).Clone()));
                    retval.ForEach(x => x.Password = string.Empty);
                    return retval.ToArray();
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        #endregion

        void IDisposable.Dispose()
        {
            this.ShutDown();
        }

    }
}